package app

import (
	"fmt"
	"log"
	"reflect"
	"regexp"

	"gopkg.in/ini.v1"
)

type conf struct {
	FontSize      float32
	FontWeight    float32
	LineHeight    float32
	LetterSpacing float32

	FontColor       string
	BackgroundColor string
	TitleColor      string
	ProgressColor   string
}

const (
	fontSizeMax     = 30
	fontSizeMin     = 12
	fontSizeStep    = 2
	fontSizeDefault = 20

	fontWeightMax     = 900
	fontWeightMin     = 100
	fontWeightStep    = 100
	fontWeightDefault = 400

	lineHeightMax     = 3
	lineHeightMin     = 1
	lineHeightStep    = 0.2
	lineHeightDefault = 1.8

	letterSpacingMax     = 2
	letterSpacingMin     = 0
	letterSpacingStep    = 0.2
	letterSpacingDefault = 0

	fontColorDefault       = "111111"
	backgroundColorDefault = "eeeeee"
	titleColorDefault      = "665f5f"
	progressColorDefault   = "111111"
)
const iniPath = "config.ini"

var globalConfig = conf{
	FontSize:      fontSizeDefault,
	LineHeight:    lineHeightDefault,
	LetterSpacing: letterSpacingDefault,
	FontWeight:    fontWeightDefault,

	FontColor:       fontColorDefault,
	BackgroundColor: backgroundColorDefault,
	TitleColor:      titleColorDefault,
	ProgressColor:   progressColorDefault,
}

var cfg *ini.File

var floatMap = map[string]struct {
	Default float32
	Min     float32
	Max     float32
	Step    float32
}{
	"FontSize":      {fontSizeDefault, fontSizeMin, fontSizeMax, fontSizeStep},
	"LineHeight":    {lineHeightDefault, lineHeightMin, lineHeightMax, lineHeightStep},
	"LetterSpacing": {letterSpacingDefault, letterSpacingMin, letterSpacingMax, letterSpacingStep},
	"FontWeight":    {fontWeightDefault, fontWeightMin, fontWeightMax, fontWeightStep},
}

var colorMap = map[string]string{
	"FontColor":       fontColorDefault,
	"BackgroundColor": backgroundColorDefault,
	"TitleColor":      titleColorDefault,
	"ProgressColor":   progressColorDefault,
}

func init() {
	var err error
	cfg, err = ini.Load(iniPath)
	if err != nil {
		cfg = ini.Empty()
		ini.ReflectFrom(cfg, &globalConfig)
		err = cfg.SaveTo(iniPath)
		if err != nil {
			panic(err)
		}
		log.Println("load config.ini failed, use default config")
	}
	err = cfg.MapTo(&globalConfig)
	if err != nil {
		panic(err)
	}
}
func saveAll() error {
	if err := ini.ReflectFrom(cfg, &globalConfig); err != nil {
		return err
	}
	return cfg.SaveTo(iniPath)
}

func saveSingle(key string, value string) error {
	// return cfg.SaveTo(iniPath)
	cfg.Section("").Key(key).SetValue(value)
	return cfg.SaveTo(iniPath)
}

func getFieldValue(config interface{}, fieldName string) (interface{}, error) {
	v := reflect.ValueOf(config)

	// 确保传入的是一个结构体指针
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	} else {
		return nil, fmt.Errorf("expected a pointer to a struct")
	}

	// 获取字段值
	field := v.FieldByName(fieldName)
	if !field.IsValid() {
		return nil, fmt.Errorf("no such field: %s in config", fieldName)
	}

	return field.Interface(), nil
}

func setFieldValue(config interface{}, fieldName string, value interface{}) error {
	v := reflect.ValueOf(config)

	// 确保传入的是一个结构体指针
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	} else {
		return fmt.Errorf("expected a pointer to a struct")
	}

	// 获取字段值
	field := v.FieldByName(fieldName)
	if !field.IsValid() {
		return fmt.Errorf("no such field: %s in config", fieldName)
	}

	// 设置字段值
	if field.CanSet() {
		field.Set(reflect.ValueOf(value))
		return nil
	}

	return fmt.Errorf("cannot set field: %s", fieldName)
}

func (a *app) GetAllConfig() conf {
	return conf{
		FontSize:      globalConfig.FontSize,
		LineHeight:    globalConfig.LineHeight,
		LetterSpacing: globalConfig.LetterSpacing,
		FontWeight:    globalConfig.FontWeight,

		FontColor:       "#" + globalConfig.FontColor,
		BackgroundColor: "#" + globalConfig.BackgroundColor,
		TitleColor:      "#" + globalConfig.TitleColor,
		ProgressColor:   "#" + globalConfig.ProgressColor,
	}
}

func (a *app) ResetAllConfig() (conf, error) {
	globalConfig = conf{
		FontSize:      fontSizeDefault,
		LineHeight:    lineHeightDefault,
		LetterSpacing: letterSpacingDefault,
		FontWeight:    fontWeightDefault,

		FontColor:       fontColorDefault,
		BackgroundColor: backgroundColorDefault,
		TitleColor:      titleColorDefault,
		ProgressColor:   progressColorDefault,
	}
	return conf{
		FontSize:      globalConfig.FontSize,
		LineHeight:    globalConfig.LineHeight,
		LetterSpacing: globalConfig.LetterSpacing,
		FontWeight:    globalConfig.FontWeight,

		FontColor:       "#" + globalConfig.FontColor,
		BackgroundColor: "#" + globalConfig.BackgroundColor,
		TitleColor:      "#" + globalConfig.TitleColor,
		ProgressColor:   "#" + globalConfig.ProgressColor,
	}, saveAll()
}

func (a *app) GetConfig(key string) (string, error) {
	if _, ok := floatMap[key]; ok {
		v, err := getFieldValue(&globalConfig, key)
		if err != nil {
			return "", err
		}
		return fmt.Sprintf("%.1f", v), nil
	}
	if _, ok := colorMap[key]; ok {
		v, err := getFieldValue(&globalConfig, key)
		if err != nil {
			return "", err
		}
		return fmt.Sprintf("#%s", v), nil
	}
	return "", fmt.Errorf("no such key: %s", key)
}

var regColor = regexp.MustCompile(`^#[0-9a-fA-F]{6}$`)

// Set color value
func (a *app) SetConfig(key, value string) error {
	if _, ok := colorMap[key]; ok {
		if !regColor.MatchString(value) {
			return fmt.Errorf("invalid color value: %s", value)
		}
		if err := setFieldValue(&globalConfig, key, value[1:]); err != nil {
			return err
		}
		return saveSingle(key, value[1:])
	}
	return fmt.Errorf("no such key: %s", key)
}

// Increase float value
func (a *app) IncConfig(key string) (string, error) {
	if p, ok := floatMap[key]; ok {
		v, err := getFieldValue(&globalConfig, key)
		if err != nil {
			return "", err
		}
		vf := v.(float32)
		if vf+p.Step > p.Max {
			return "", fmt.Errorf("value out of range")
		}
		vf += p.Step
		err = setFieldValue(&globalConfig, key, vf)
		if err != nil {
			return "", err
		}
		vr := fmt.Sprintf("%.1f", vf)
		return vr, saveSingle(key, vr)
	}
	return "", fmt.Errorf("no such key: %s", key)
}

// Decrease float value
func (a *app) DecConfig(key string) (string, error) {
	if p, ok := floatMap[key]; ok {
		v, err := getFieldValue(&globalConfig, key)
		if err != nil {
			return "", err
		}
		vf := v.(float32)
		if vf-p.Step < p.Min {
			return "", fmt.Errorf("value out of range")
		}
		vf -= p.Step
		err = setFieldValue(&globalConfig, key, vf)
		if err != nil {
			return "", err
		}
		vr := fmt.Sprintf("%.1f", vf)
		return vr, saveSingle(key, vr)
	}
	return "", fmt.Errorf("no such key: %s", key)
}

// Reset value
func (a *app) ResetConfig(key string) (string, error) {
	if p, ok := floatMap[key]; ok {
		err := setFieldValue(&globalConfig, key, p.Default)
		if err != nil {
			return "", err
		}
		vr := fmt.Sprintf("%.1f", p.Default)
		return vr, saveSingle(key, vr)
	}
	if v, ok := colorMap[key]; ok {
		err := setFieldValue(&globalConfig, key, v)
		if err != nil {
			return "", err
		}
		vr := fmt.Sprintf("#%s", v)
		return vr, saveSingle(key, vr)
	}
	return "", fmt.Errorf("no such key: %s", key)
}
